"""Tests for error handling and fallback mechanisms."""
import pytest
from unittest.mock import Mock, patch
from app.utils.error_handler import (
    retry_with_exponential_backoff,
    get_user_friendly_error_message,
    ErrorResponse,
    NetworkError,
    APIError
)
from app.services.fallback_service import FallbackService
from app.models.poi import POI
from app.models.weather_data import WeatherData
from datetime import datetime


class TestErrorHandler:
    """Test error handler utilities."""
    
    def test_retry_with_exponential_backoff_success(self):
        """Test retry decorator succeeds on first attempt."""
        call_count = 0
        
        @retry_with_exponential_backoff(max_retries=3)
        def successful_function():
            nonlocal call_count
            call_count += 1
            return "success"
        
        result = successful_function()
        assert result == "success"
        assert call_count == 1
    
    def test_retry_with_exponential_backoff_eventual_success(self):
        """Test retry decorator succeeds after failures."""
        call_count = 0
        
        @retry_with_exponential_backoff(max_retries=3, initial_delay=0.01)
        def eventually_successful_function():
            nonlocal call_count
            call_count += 1
            if call_count < 3:
                raise NetworkError("Temporary failure")
            return "success"
        
        result = eventually_successful_function()
        assert result == "success"
        assert call_count == 3
    
    def test_retry_with_exponential_backoff_all_fail(self):
        """Test retry decorator fails after all retries."""
        call_count = 0
        
        @retry_with_exponential_backoff(max_retries=3, initial_delay=0.01)
        def always_failing_function():
            nonlocal call_count
            call_count += 1
            raise NetworkError("Permanent failure")
        
        with pytest.raises(NetworkError):
            always_failing_function()
        
        assert call_count == 3
    
    def test_get_user_friendly_error_message_network(self):
        """Test user-friendly message for network errors."""
        error = NetworkError("Connection failed")
        message = get_user_friendly_error_message(error)
        assert "网络" in message
    
    def test_get_user_friendly_error_message_api_401(self):
        """Test user-friendly message for 401 error."""
        error = APIError("401 Unauthorized")
        message = get_user_friendly_error_message(error)
        assert "认证" in message or "登录" in message
    
    def test_get_user_friendly_error_message_api_500(self):
        """Test user-friendly message for 500 error."""
        error = APIError("500 Internal Server Error")
        message = get_user_friendly_error_message(error)
        assert "服务" in message
    
    def test_error_response_create(self):
        """Test ErrorResponse creation."""
        error = NetworkError("Test error")
        response = ErrorResponse.create(error, include_details=False)
        
        assert response['success'] is False
        assert 'error' in response
        assert 'message' in response['error']
        assert 'type' in response['error']
        assert 'details' not in response['error']
    
    def test_error_response_create_with_details(self):
        """Test ErrorResponse creation with details."""
        error = NetworkError("Test error")
        response = ErrorResponse.create(error, include_details=True)
        
        assert response['success'] is False
        assert 'details' in response['error']


class TestFallbackService:
    """Test fallback service."""
    
    def test_generate_poi_introduction_fallback(self):
        """Test POI introduction fallback generation."""
        fallback_service = FallbackService()
        
        poi = POI(
            name='测试公园',
            latitude=39.9,
            longitude=116.4,
            category='公园',
            description='一个美丽的公园'
        )
        
        context = {
            'weather': {
                'condition': '晴',
                'temperature': 25
            }
        }
        
        introduction = fallback_service.generate_poi_introduction_fallback(poi, context)
        
        assert isinstance(introduction, str)
        assert len(introduction) > 0
        assert '测试公园' in introduction
        assert '晴' in introduction or '阳光' in introduction
    
    def test_generate_poi_introduction_fallback_no_weather(self):
        """Test POI introduction fallback without weather."""
        fallback_service = FallbackService()
        
        poi = POI(
            name='测试博物馆',
            latitude=39.9,
            longitude=116.4,
            category='博物馆',
            description='历史博物馆'
        )
        
        introduction = fallback_service.generate_poi_introduction_fallback(poi, None)
        
        assert isinstance(introduction, str)
        assert len(introduction) > 0
        assert '测试博物馆' in introduction
    
    def test_get_preset_music_park(self):
        """Test preset music selection for park."""
        fallback_service = FallbackService()
        
        poi = POI(
            name='测试公园',
            latitude=39.9,
            longitude=116.4,
            category='公园',
            description='公园'
        )
        
        music = fallback_service.get_preset_music(poi)
        
        assert isinstance(music, dict)
        assert 'name' in music
        assert 'url' in music
        assert 'duration' in music
        assert music['duration'] > 0
    
    def test_get_preset_music_museum(self):
        """Test preset music selection for museum."""
        fallback_service = FallbackService()
        
        poi = POI(
            name='测试博物馆',
            latitude=39.9,
            longitude=116.4,
            category='博物馆',
            description='博物馆'
        )
        
        music = fallback_service.get_preset_music(poi)
        
        assert isinstance(music, dict)
        assert 'classical' in music['url'] or '古典' in music['name']
    
    def test_get_preset_music_with_weather(self):
        """Test preset music selection with weather context."""
        fallback_service = FallbackService()
        
        poi = POI(
            name='测试广场',
            latitude=39.9,
            longitude=116.4,
            category='广场',
            description='广场'
        )
        
        weather = WeatherData(
            temperature=25.0,
            condition='晴',
            humidity=60,
            wind_speed=10.0,
            aqi=50,
            timestamp=datetime.utcnow()
        )
        
        music = fallback_service.get_preset_music(poi, weather)
        
        assert isinstance(music, dict)
        # Sunny weather should prefer upbeat music
        assert 'upbeat' in music['url'] or '活力' in music['name']
    
    def test_normalize_category(self):
        """Test category normalization."""
        fallback_service = FallbackService()
        
        assert fallback_service._normalize_category('风景名胜') == '风景名胜'
        assert fallback_service._normalize_category('公园广场') == '公园'
        assert fallback_service._normalize_category('博物馆') == '博物馆'
        assert fallback_service._normalize_category('其他类型') == '其他'
    
    def test_log_failure(self):
        """Test failure logging."""
        fallback_service = FallbackService()
        
        # Should not raise exception
        error = Exception("Test error")
        fallback_service.log_failure('test_service', error, {'test': 'context'})


if __name__ == '__main__':
    pytest.main([__file__, '-v'])
